﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Driver;
using System.Linq.Expressions;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using System.Reflection.Emit;
using Colorful.Models;

namespace Colorful.Database
{
    #region 自增Id

    #region Ids
    /// <summary>
    /// 生成自增Id
    /// </summary>
    public class Ids
    {
        /// <summary>
        /// 表名
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// 自增Id
        /// </summary>
        public long Index { get; set; }
    }
    #endregion

    #region Ids Extensions
    public static class IdsExtensions
    {
        /// <summary>
        /// 获取自增Id
        /// </summary>
        /// <typeparam name="TDocument"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static long GetId<TDocument>(this IMongoCollection<TDocument> collection)
        {
            var tableName = collection.CollectionNamespace.CollectionName;
            var idsCol = collection.Database.GetCollection<BsonDocument>("Ids", collection.Settings);
            var filter = Builders<BsonDocument>.Filter.Eq("_id", tableName);
            long inc = 1;
            var update = Builders<BsonDocument>.Update.Inc("Index", inc);
            var ids = idsCol.FindOneAndUpdate(filter, update, new FindOneAndUpdateOptions<BsonDocument, Ids>()
            {
                IsUpsert = true,
                ReturnDocument = ReturnDocument.After
            });
            return ids.Index;
        }
        /// <summary>
        /// 获取自增Id
        /// </summary>
        /// <typeparam name="TDocument"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static int GetIntId<TDocument>(this IMongoCollection<TDocument> collection)
        {
            return (int)collection.GetId<TDocument>();
        }
    }
    #endregion

    #endregion

    #region 缓存
    public class Cache
    {
        /// <summary>
        /// Key
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string Value { get; set; }
        /// <summary>
        /// 过期时间
        /// </summary>
        public DateTime? ExpireDate { get; set; }
        /// <summary>
        /// 添加日期
        /// </summary>
        public DateTime AddDate { get; set; }
    }
    #endregion

    #region MongodbContext
    public partial class MongodbContext : IDisposable
    {
        private MongoClient _client;
        private IMongoDatabase _db;
        private MongoCollectionSettings _collectionSettings;
        private static bool IsInit = false;

        #region 属性
        /// <summary>
        /// 数据表前辍
        /// </summary>
        public string CollectionPrefix { get; set; }
        public MongoClient Client
        {
            get
            {
                return _client;
            }
        }
        public IMongoDatabase Database
        {
            get
            {
                return _db;
            }
        }
        public MongoCollectionSettings CollectionSettings
        {
            get
            {
                return _collectionSettings;
            }
            set
            {
                _collectionSettings = value;
            }
        }
        #endregion

        #region Collections
        public IMongoCollection<Cache> Caches
        {
            get
            {
                return _db.GetCollection<Cache>("Cache");
            }
        }
        #endregion

        #region 初始化
        public MongodbContext(string server, string username, string password, string dbName)
            : this($"mongodb://{server}:{username}@{password}/{dbName}", dbName)
        {
        }
        public MongodbContext(string connStr, string dbName, string collectionPrefix = "")
        {
            this.CollectionPrefix = collectionPrefix;
            if (!IsInit)
            {
                IsInit = true;
                BsonSerializer.RegisterSerializer(typeof(DateTime), DateTimeSerializer.LocalInstance);
            }
            this.Init();
            _collectionSettings = new MongoCollectionSettings();
            _client = new MongoClient(connStr);
            _db = _client.GetDatabase(dbName);
        }
        #endregion

        #region Virtual Method
        partial void Init();
        #endregion

        #region Public Method
        public long GetId(string tableName)
        {
            var idsCol = this.Database.GetCollection<BsonDocument>("Ids", this.CollectionSettings);
            var filter = Builders<BsonDocument>.Filter.Eq("_id", tableName);
            long inc = 1;
            var update = Builders<BsonDocument>.Update.Inc("Index", inc);
            var ids = idsCol.FindOneAndUpdate(filter, update, new FindOneAndUpdateOptions<BsonDocument, Ids>()
            {
                IsUpsert = true,
                ReturnDocument = ReturnDocument.After
            });
            return ids.Index;
        }
        public string GetObjectId()
        {
            return MongoDB.Bson.ObjectId.GenerateNewId().ToString();
        }
        public string GetUniqueId()
        {
            return ObjectId.GenerateNewId().ToString();
        }
        /// <summary>
        /// 缓存
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">值</param>
        /// <param name="absoluteExpiration">过期时间</param>
        public void SetCache(string key, string value, TimeSpan? absoluteExpiration = null)
        {
            if (absoluteExpiration == null)
                this.Caches.Update1(a => a.Id == key, b => b.Value, value, b => b.AddDate, DateTime.Now, true);
            else
            {
                var expireDate = DateTime.Now.Add(absoluteExpiration.Value);
                this.Caches.Update1(a => a.Id == key, this.Caches.GetUpdate(a => a.Value, value).Set(b => b.AddDate, DateTime.Now).Set(b => b.ExpireDate, expireDate), true);
            }
        }
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="updateExpirationDate">是否更新过期时间</param>
        /// <returns></returns>
        public string GetCache(string key, bool updateExpirationDate = false)
        {
            var cache = this.Caches.FirstOrDefault(a => a.Id == key);
            if (cache == null)
                return null;
            else
            {
                if (cache.ExpireDate != null)
                {
                    if (cache.ExpireDate < DateTime.Now)
                    {
                        this.Caches.Delete(a => a.Id == key);
                        return null;
                    }
                    else
                    {
                        var timeInterval = cache.ExpireDate.Value - cache.AddDate;
                        var date = DateTime.Now.Add(timeInterval);
                        this.Caches.UpdateByPK(key, b => b.ExpireDate, date);
                    }
                }
                return cache.Value;
            }
        }
        #endregion

        #region GetCollection
        protected IMongoCollection<T> GetCollection<T>(string name)
        {
            if (!string.IsNullOrEmpty(this.CollectionPrefix))
                name = $"{this.CollectionPrefix}_{name}";
            return _db.GetCollection<T>(name);
        }
        #endregion

        #region Dispose
        public void Dispose()
        {
            _collectionSettings = null;
            _client = null;
            _db = null;
        }
        #endregion
    }
    #endregion
}
